package com.ruoyi.common.elasticsearch.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.elasticsearch.domain.EsSkuInfo;
import com.ruoyi.common.elasticsearch.domain.SearchParam;
import com.ruoyi.common.elasticsearch.domain.SearchResult;
import com.ruoyi.common.elasticsearch.service.ElasticSearchService;
import com.ruoyi.config.ElasticsearchConfig;
import com.ruoyi.utils.ResponseResult;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @ClassName ElasticSearchServiceImpl
 * @Description
 * @Author huangshun
 * @Date 2021/1/22 0022 下午 6:58
 */
@Service
public class ElasticSearchServiceImpl implements ElasticSearchService {


    @Autowired
    private RestHighLevelClient elasticsearchClient;
    /*
     * 搜索引擎索引
     */
    public static final String ES_MAPPING = "yuanju_product_v1";
    /*
     * 搜索引擎分页，每页条数
     */
    public static final Integer ES_PAGENUM = 20;

    @Override
    public boolean saveEs(List<EsSkuInfo> skuInfoList) {
        try {
            for (EsSkuInfo es: skuInfoList){
                IndexRequest request = new IndexRequest(ES_MAPPING).id(es.getSkuId());
                request.source(JSON.toJSONString(es), XContentType.JSON);
                IndexResponse index = elasticsearchClient.index(request, RequestOptions.DEFAULT);
                System.out.println(index);
                return true;

            }

        }catch (Exception e){
            System.out.println("保存es出错:"+e.getMessage());
        }
        return false;
    }

//    @Override
//    public boolean saveEs(List<EsSkuInfo> skuInfoList) {
//        try {
//            //保存到ES 中
//            // 执行操作
//            BulkRequest bulkRequest = new BulkRequest();
//            //构造保存请求
//            skuInfoList.forEach(skuInfo -> {
//                IndexRequest indexRequest = new IndexRequest(ES_MAPPING);
//                indexRequest.id(skuInfo.getSkuId());
//
//                String jsonString = JSONUtil.toJsonPrettyStr(skuInfo);
//                indexRequest.source(jsonString, XContentType.JSON);
//                bulkRequest.add(indexRequest);
//            });
//            BulkResponse bulk = elasticsearchClient.bulk(bulkRequest, ElasticsearchConfig.COMMON_OPTIONS);
//
//            // 如果批量错误
//            boolean failures = bulk.hasFailures();
//            List<String> errorIds = Arrays.stream((bulk.getItems())).map(item -> {
//                System.out.println(item.getFailureMessage());
//                return item.getId();
//            }).collect(Collectors.toList());
//
//            return true;
//        }catch (Exception e){
//            System.out.println("保存es出错:"+e.getMessage());
//        }
//        return false;
//    }

    @Override
    public void updateEs(EsSkuInfo skuInfo) {

    }

    @Override
    public void deleteEs(String id) {
        try {
            DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(ES_MAPPING);
            deleteByQueryRequest.setConflicts("proceed");
            // 设置查询条件，第一个参数是字段名，第二个参数是字段的值
            deleteByQueryRequest.setQuery(new TermQueryBuilder("spuId", id));
            // 更新最大文档数
            deleteByQueryRequest.setSize(100);
            // 批次大小
            deleteByQueryRequest.setBatchSize(1000);
            // 并行
            deleteByQueryRequest.setSlices(2);
            // 超时
            deleteByQueryRequest.setTimeout(TimeValue.timeValueMinutes(2));
            // 刷新索引
            deleteByQueryRequest.setRefresh(true);

            elasticsearchClient.deleteByQuery(deleteByQueryRequest, ElasticsearchConfig.COMMON_OPTIONS);
        } catch (IOException e) {
            System.out.println(e.getMessage());
            e.printStackTrace();
        }
    }

    @Override
    public SearchResult searchEs(SearchParam searchParam) {
        if(searchParam.getPageNum() == null){
            searchParam.setPageNum(1);
        }
        //1 创建检索请求
        SearchRequest searchRequest = builderSearchRequest(searchParam);
        try {
            //2 执行检索
            SearchResponse searchResponse = elasticsearchClient.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
            //3 分析结果
            SearchResult  result = builderSearchResult(searchResponse,searchParam.getPageNum());
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public ResponseResult searchCategory(String CategoryName) {
        SearchRequest searchRequest = new SearchRequest();
        //指定索引
        searchRequest.indices(ES_MAPPING);
        //指定DSL,检索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // TODO 1.模糊查询  过滤（按分类，品牌，属性，价格区间，库存）
        //构建boolquery
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if(!StringUtils.isEmpty(CategoryName)){
            boolQuery.must(QueryBuilders.matchQuery("alias",CategoryName));
        }
        searchSourceBuilder.query(boolQuery);
        //3.2 分类聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalog_agg");
                 catalogAgg
                .field("catalogId")
                .size(ES_PAGENUM)
                .order(BucketOrder.count(false));
        //分类子聚合
        catalogAgg.subAggregation(AggregationBuilders.terms("catalog_agg_name").field("catalogName").size(1));
        searchSourceBuilder.aggregation(catalogAgg);
        searchRequest.source(searchSourceBuilder);
        try {
            //2 执行检索
            SearchResponse searchResponse = elasticsearchClient.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
            //3 分析结果

            //商品分类
            List<Object> catalogs=new ArrayList<>();
            ParsedStringTerms catalog_agg = searchResponse.getAggregations().get("catalog_agg");
            List<? extends Terms.Bucket> catalogBuckets = catalog_agg.getBuckets();
            catalogBuckets.forEach(bucket -> {
                ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
                map.put("catalogId",bucket.getKeyAsString());
                ParsedStringTerms catalog_agg_name = bucket.getAggregations().get("catalog_agg_name");
                map.put("catalogName",catalog_agg_name.getBuckets().get(0).getKeyAsString());
                map.put("count",bucket.getDocCount());
                catalogs.add(map);
            });
            return ResponseResult.success(catalogs);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * TODO 模糊查询  过滤（按分类，品牌，属性，价格区间，库存），排序，分页，高亮，聚合分析
     * @return
     */
    private SearchRequest builderSearchRequest(SearchParam searchParam) {

        //指定DSL,检索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // TODO 1.模糊查询  过滤（按分类，品牌，属性，价格区间，库存）
        //构建boolquery
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        //1.1 must 模糊查询
        if(!StringUtils.isEmpty(searchParam.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("skuTitle",searchParam.getKeyword()));
        }
        //1.2 filter
        //1.2.1  按照标准词
        if(!StringUtils.isEmpty(searchParam.getCatalogId())){
            boolQuery.filter(QueryBuilders.termQuery("catalogId",searchParam.getCatalogId()));
        }
        if (org.apache.commons.lang.StringUtils.isNotBlank(searchParam.getCatalogIdList())){
            String[] data = searchParam.getCatalogIdList().split(",");
            boolQuery.must(QueryBuilders.termsQuery("catalogId",data));
        }
        //1.2.2 按照品牌id
        if(searchParam.getBrandId() != null && searchParam.getBrandId().size()>0){
            boolQuery.filter(QueryBuilders.termsQuery("brandId",searchParam.getBrandId()));
        }
        //1.2.2 按照类型（服务、商品）
        if(searchParam.getSkuType() != null ){
            boolQuery.must(QueryBuilders.termsQuery("skuType",searchParam.getSkuType().toString()));
        }
        //1.2.3 按照所有属性
        if(searchParam.getAttrs() != null && searchParam.getAttrs().size()>0){
            // 值格式为  大小_5寸:6寸
            searchParam.getAttrs().forEach(attr ->{
                String[] s = attr.split("_");
                String attrName = s[0];
                String[] attrValue = s[1].split(":");
                BoolQueryBuilder nestedboolQueryBuilder = QueryBuilders.boolQuery();
                nestedboolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrName",attrName));
                nestedboolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue",attrValue));
                //每一个都必须生成一个nestedQuery
                boolQuery.filter(QueryBuilders.nestedQuery("attrs", nestedboolQueryBuilder, ScoreMode.None));
            });
        }

        //1.2.4 按照价格区间
        if(!StringUtils.isEmpty(searchParam.getPrice())){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("brandId");
            String[] arrayPrice = searchParam.getPrice().split("_");
            if(arrayPrice.length == 2){
                rangeQuery.gte(arrayPrice[0]).lte(arrayPrice[1]);
            }else if(arrayPrice.length == 1){
                if(searchParam.getPrice().startsWith("_")){
                    rangeQuery.lte(arrayPrice[0]);
                }
                if(searchParam.getPrice().endsWith("_")){
                    rangeQuery.gte(arrayPrice[0]);
                }
            }
            boolQuery.filter(rangeQuery);
        }
        //1.2.5 是否包邮
        if(StringUtils.isNotBlank(searchParam.getIsPostage())){
            boolQuery.filter(QueryBuilders.termQuery("isPostage",searchParam.getIsPostage()));
        }
        //1.2.6 是否包含服务
        if(StringUtils.isNotBlank(searchParam.getIsService())){
            boolQuery.filter(QueryBuilders.termQuery("isService",searchParam.getIsService()));
        }
        //1.2.8 是否二手
        if(StringUtils.isNotBlank(searchParam.getIsTwoSpu())){
            boolQuery.filter(QueryBuilders.termQuery("isTwoSpu",searchParam.getIsTwoSpu()));
        }
        //1.2.9 根据skuId
        if(searchParam.getSkuId() != null && searchParam.getSkuId().size()>0){
            boolQuery.filter(QueryBuilders.termsQuery("skuId",searchParam.getSkuId()));
        }

        //1.2.9 根据 支付方式
        if(StringUtils.isNotBlank(searchParam.getPaymentTerm())){
            boolQuery.filter(QueryBuilders.termsQuery("paymentTerm",searchParam.getPaymentTerm()));
        }

        //1.2.11  按照热词标签
        if(searchParam.getHotWordLables() != null && searchParam.getHotWordLables().size()>0){
            // 值格式为  id_标签名
            searchParam.getHotWordLables().forEach(attr ->{
                if(StringUtils.isNotBlank(attr)){
                    String[] s = attr.split("_");
                    String areaId = s[0];
                    String[] timeValue = s[1].split(":");
                    BoolQueryBuilder nestedboolQueryBuilder = QueryBuilders.boolQuery();
                    nestedboolQueryBuilder.must(QueryBuilders.termQuery("hotWordLables.hotId",areaId));
                    nestedboolQueryBuilder.must(QueryBuilders.termsQuery("hotWordLables.hotName",timeValue));
                    //每一个都必须生成一个nestedQuery
                    boolQuery.filter(QueryBuilders.nestedQuery("hotWordLables", nestedboolQueryBuilder, ScoreMode.None));
                }
            });
        }
        if(StringUtils.isNotBlank(searchParam.getDeliveryTime())){
            boolQuery.filter(QueryBuilders.termQuery("deliveryTime",searchParam.getDeliveryTime()));
        }

        if (searchParam.getSpuAddType() != null){
            boolQuery.must(QueryBuilders.termQuery("spuAddType", searchParam.getSpuAddType()));
        }

        if(StringUtils.isBlank(searchParam.getIsPriceSort())){
//            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
//                    boolQuery,
//                    new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
//                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
//                                    QueryBuilders.termQuery("isStick", true),
//                                    ScoreFunctionBuilders.weightFactorFunction(10)
//                            )
//                    }
//            );
//            //把查询条件封装
//            searchSourceBuilder.query(functionScoreQueryBuilder);

            searchSourceBuilder.query(boolQuery);
            searchSourceBuilder.sort("spuSort", SortOrder.ASC);
        }else {
            searchSourceBuilder.query(boolQuery);
            searchSourceBuilder.sort("skuPrice", SortOrder.ASC);
        }



        //TODO 2. 排序，分页，高亮
        //2.1 排序 -- （字段，排序方式asc/desc）
//        searchSourceBuilder.sort("spuId", SortOrder.DESC);
        //2.2 分页
        if(searchParam.getPageNum()>0 ){
            Integer from = (searchParam.getPageNum()-1)*ES_PAGENUM;
            searchSourceBuilder.from(from).size(ES_PAGENUM);
        }
        //2.3 高亮
        if(!StringUtils.isEmpty(searchParam.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style = 'color: red' >"); //前置标签
            highlightBuilder.postTags("</b>"); // 后置标签
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        //TODO 3.聚合分析
        //3.1 品牌聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg");
        brandAgg.field("brandId").size(ES_PAGENUM);
        //品牌子聚合
        brandAgg.subAggregation(AggregationBuilders.terms("brand_agg_name").field("brandName").size(1));
        searchSourceBuilder.aggregation(brandAgg);

        //3.2 分类聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalog_agg");
        catalogAgg.field("catalogId").size(ES_PAGENUM);
        //分类子聚合
        catalogAgg.subAggregation(AggregationBuilders.terms("catalog_agg_name").field("catalogName").size(1));
        searchSourceBuilder.aggregation(catalogAgg);

        //3.3 属性聚合
        NestedAggregationBuilder nestedAgg = AggregationBuilders.nested("attr_agg", "attrs");
        //子聚合
        TermsAggregationBuilder attr_name_agg = AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(10);
        //子聚合对应的聚合属性值名称和value
        attr_name_agg.subAggregation(AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(1));
        attr_name_agg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(10));

        nestedAgg.subAggregation(attr_name_agg);
        searchSourceBuilder.aggregation(nestedAgg);
        System.out.println(searchSourceBuilder.toString());
        SearchRequest searchRequest = new SearchRequest();
        //指定索引
        searchRequest.indices(ES_MAPPING);
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    /**
     * 方法名 builderSearchResult
     * 描述 TODO 结果整合
     * 作者 黄顺
     * 时间 2021/1/25 0025 下午 4:03
     * @Param [searchResponse, pageNum]
     */
    private SearchResult builderSearchResult(SearchResponse searchResponse, int pageNum) {
        SearchResult searchResult = new SearchResult();

        SearchHits hits = searchResponse.getHits();

        // 1.商品品牌
        List<Object> brands=new ArrayList<>();
        ParsedStringTerms brand_agg = searchResponse.getAggregations().get("brand_agg");
        List<? extends Terms.Bucket> buckets = brand_agg.getBuckets();
        buckets.forEach(bucket -> {
            ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
            map.put("brandId",bucket.getKeyAsString());
            ParsedStringTerms brand_agg_name = bucket.getAggregations().get("brand_agg_name");
            map.put("brandName",brand_agg_name.getBuckets().get(0).getKeyAsString());
            brands.add(map);
        });
        searchResult.setBrands(brands);
        //商品属性

        List<Object> attrs= new ArrayList<>();
        ParsedNested attr_agg = searchResponse.getAggregations().get("attr_agg");
        ParsedStringTerms attr_id_agg = attr_agg.getAggregations().get("attr_name_agg");
        List<? extends Terms.Bucket> attr_id_aggBuckets = attr_id_agg.getBuckets();
        attr_id_aggBuckets.forEach(bucket -> {
            ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
            //1.属性名字
            map.put("attrName",bucket.getKeyAsString());
            //2.属性值
            ParsedStringTerms attr_value_agg = bucket.getAggregations().get("attr_value_agg");
            List<Object> attrValue =new ArrayList<>();
            attr_value_agg.getBuckets().forEach(bucket1 -> {
                attrValue.add(bucket1.getKeyAsString());
            });
            map.put("attrValue",attrValue);
            attrs.add(map);

        });
        searchResult.setAttrs(attrs);
        //商品分类
        List<Object> catalogs=new ArrayList<>();
        ParsedStringTerms catalog_agg = searchResponse.getAggregations().get("catalog_agg");
        List<? extends Terms.Bucket> catalogBuckets = catalog_agg.getBuckets();
        catalogBuckets.forEach(bucket -> {
            ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<>();
            map.put("catalogId",bucket.getKeyAsString());
            ParsedStringTerms catalog_agg_name = bucket.getAggregations().get("catalog_agg_name");
            map.put("catalogName",catalog_agg_name.getBuckets().get(0).getKeyAsString());
            catalogs.add(map);
        });
        searchResult.setCatalogs(catalogs);
        //当前页
        searchResult.setPageNum(pageNum);
        //总记录数
        long total = hits.getTotalHits().value;
        searchResult.setTotal(total);
        //总页数
        int totalPage =(int)total%ES_PAGENUM ==0?(int)total/ES_PAGENUM:((int)total/ES_PAGENUM+1);
        searchResult.setTotalPages(totalPage);
        //产品信息
        SearchHit[] searchHits = hits.getHits();
        Aggregations aggregations = searchResponse.getAggregations();
        List<EsSkuInfo> skuInfos = new ArrayList<>();
        for (SearchHit hit :searchHits){
            JSONObject jsonObject = JSONUtil.parseObj(hit.getSourceAsMap());
            EsSkuInfo esSkuInfo = BeanUtil.toBean(jsonObject, EsSkuInfo.class);
            skuInfos.add(esSkuInfo);
        }
        searchResult.setProducts(skuInfos);

        return searchResult;
    }
}
